import React, { FunctionComponent, useEffect, useState } from 'react';
import { Field, Form, Formik } from 'formik';
import Drawer, { DrawerControlledDialProps, DrawerVariant } from '@components/common/drawer/Drawer';
import Button from '@mui/material/Button';
import MenuItem from '@mui/material/MenuItem';
import * as Yup from 'yup';
import { graphql } from 'react-relay';
import { FormikConfig, FormikHelpers } from 'formik/dist/types';
import { RecordSourceSelectorProxy } from 'relay-runtime';
import SwitchField from 'src/components/fields/SwitchField';
import { VulnerabilitiesLinesPaginationQuery$variables } from '@components/arsenal/__generated__/VulnerabilitiesLinesPaginationQuery.graphql';
import { useFormatter } from '../../../../components/i18n';
import { handleErrorInForm } from '../../../../relay/environment';
import TextField from '../../../../components/TextField';
import CreatedByField from '../../common/form/CreatedByField';
import ObjectLabelField from '../../common/form/ObjectLabelField';
import ObjectMarkingField from '../../common/form/ObjectMarkingField';
import MarkdownField from '../../../../components/fields/MarkdownField';
import { Option } from '../../common/form/ReferenceField';
import { insertNode } from '../../../../utils/store';
import { fieldSpacingContainerStyle } from '../../../../utils/field';
import ConfidenceField from '../../common/form/ConfidenceField';
import SelectField from '../../../../components/fields/SelectField';
import { ExternalReferencesField } from '../../common/form/ExternalReferencesField';
import { useSchemaCreationValidation } from '../../../../utils/hooks/useEntitySettings';
import { VulnerabilityCreationMutation, VulnerabilityCreationMutation$variables } from './__generated__/VulnerabilityCreationMutation.graphql';
import useDefaultValues from '../../../../utils/hooks/useDefaultValues';
import CustomFileUploader from '../../common/files/CustomFileUploader';
import useApiMutation from '../../../../utils/hooks/useApiMutation';
import CreateEntityControlledDial from '../../../../components/CreateEntityControlledDial';
import useHelper from '../../../../utils/hooks/useHelper';
import useBulkCommit from '../../../../utils/hooks/useBulkCommit';
import { splitMultilines } from '../../../../utils/String';
import BulkTextModal from '../../../../components/fields/BulkTextField/BulkTextModal';
import ProgressBar from '../../../../components/ProgressBar';
import BulkTextField from '../../../../components/fields/BulkTextField/BulkTextField';
import BulkTextModalButton from '../../../../components/fields/BulkTextField/BulkTextModalButton';

const vulnerabilityMutation = graphql`
  mutation VulnerabilityCreationMutation($input: VulnerabilityAddInput!) {
    vulnerabilityAdd(input: $input) {
      id
      standard_id
      name
      description
      entity_type
      parent_types
      ...VulnerabilitiesLine_node
    }
  }
`;

interface VulnerabilityAddInput {
  name: string
  description: string
  createdBy: Option | null
  objectMarking: Option[]
  objectLabel: Option[]
  x_opencti_cvss_base_score: number | undefined
  x_opencti_cvss_base_severity: string
  x_opencti_cvss_attack_vector: string
  confidence: number | null
  x_opencti_cisa_kev: boolean
  x_opencti_epss_score: number | undefined
  x_opencti_epss_percentile: number | undefined
  externalReferences: { value: string }[]
  file: File | null
}

interface VulnerabilityCreationProps {
  inputValue?: string;
  paginationOptions: VulnerabilitiesLinesPaginationQuery$variables;
}

interface VulnerabilityFormProps {
  updater: (store: RecordSourceSelectorProxy, key: string) => void;
  onReset?: () => void;
  onCompleted?: () => void;
  defaultCreatedBy?: Option;
  defaultMarkingDefinitions?: Option[];
  defaultConfidence?: number;
  inputValue?: string;
  bulkModalOpen?: boolean;
  onBulkModalClose: () => void;
}

const VULNERABILITY_TYPE = 'Vulnerability';

export const VulnerabilityCreationForm: FunctionComponent<VulnerabilityFormProps> = ({
  updater,
  onReset,
  onCompleted,
  defaultConfidence,
  defaultCreatedBy,
  defaultMarkingDefinitions,
  inputValue,
  bulkModalOpen = false,
  onBulkModalClose,
}) => {
  const { isFeatureEnable } = useHelper();
  const { t_i18n } = useFormatter();
  const [progressBarOpen, setProgressBarOpen] = useState(false);
  const basicShape = {
    name: Yup.string()
      .min(2)
      .required(t_i18n('This field is required')),
    description: Yup.string()
      .nullable(),
    x_opencti_cvss_base_score: Yup.number().min(0).max(10)
      .nullable(),
    x_opencti_cvss_base_severity: Yup.string()
      .nullable(),
    x_opencti_cvss_attack_vector: Yup.string()
      .nullable(),
    x_opencti_epss_score: Yup.number().min(0).max(1).nullable(),
    x_opencti_epss_percentile: Yup.number().min(0).max(1).nullable(),
    confidence: Yup.number()
      .nullable(),
  };
  const vulnerabilityValidator = useSchemaCreationValidation(VULNERABILITY_TYPE, basicShape);

  const [commit] = useApiMutation<VulnerabilityCreationMutation>(
    vulnerabilityMutation,
    undefined,
    { successMessage: `${t_i18n('entity_Vulnerability')} ${t_i18n('successfully created')}` },
  );
  const {
    bulkCommit,
    bulkCount,
    bulkCurrentCount,
    BulkResult,
    resetBulk,
  } = useBulkCommit<VulnerabilityCreationMutation>({
    commit,
    relayUpdater: (store) => {
      if (updater) {
        updater(store, 'vulnerabilityAdd');
      }
    },
  });

  useEffect(() => {
    if (bulkCount > 1) {
      setProgressBarOpen(true);
    }
  }, [bulkCount]);

  const onSubmit: FormikConfig<VulnerabilityAddInput>['onSubmit'] = (
    values: VulnerabilityAddInput,
    {
      setSubmitting,
      setErrors,
      resetForm,
    }: FormikHelpers<VulnerabilityAddInput>,
  ) => {
    const allNames = splitMultilines(values.name);
    const variables: VulnerabilityCreationMutation$variables[] = allNames.map((name) => ({
      input: {
        name,
        description: values.description,
        createdBy: values.createdBy?.value,
        objectMarking: values.objectMarking.map((v) => v.value),
        objectLabel: values.objectLabel.map((v) => v.value),
        x_opencti_cvss_base_score: parseFloat(String(values.x_opencti_cvss_base_score)),
        x_opencti_cvss_base_severity: values.x_opencti_cvss_base_severity,
        x_opencti_cvss_attack_vector: values.x_opencti_cvss_attack_vector,
        x_opencti_cisa_kev: values.x_opencti_cisa_kev,
        x_opencti_epss_score: parseFloat(String(values.x_opencti_epss_score)),
        x_opencti_epss_percentile: parseFloat(String(values.x_opencti_epss_percentile)),
        confidence: parseInt(String(values.confidence), 10),
        externalReferences: values.externalReferences.map(({ value }) => value),
        file: values.file,
      },
    }));

    bulkCommit({
      variables,
      onStepError: (error) => {
        handleErrorInForm(error, setErrors);
      },
      onCompleted: (total: number) => {
        setSubmitting(false);
        if (total < 2) {
          resetForm();
          onCompleted?.();
        }
      },
    });
  };

  const initialValues = useDefaultValues<VulnerabilityAddInput>(
    VULNERABILITY_TYPE,
    {
      name: inputValue ?? '',
      description: '',
      createdBy: defaultCreatedBy ?? null,
      objectMarking: defaultMarkingDefinitions ?? [],
      objectLabel: [],
      x_opencti_cvss_base_score: undefined,
      x_opencti_cvss_base_severity: '',
      x_opencti_cvss_attack_vector: '',
      confidence: defaultConfidence ?? null,
      x_opencti_cisa_kev: false,
      x_opencti_epss_score: undefined,
      x_opencti_epss_percentile: undefined,
      externalReferences: [],
      file: null,
    },
  );

  return (
    <Formik<VulnerabilityAddInput>
      initialValues={initialValues}
      validationSchema={vulnerabilityValidator}
      onSubmit={onSubmit}
      onReset={onReset}
    >
      {({
        submitForm,
        handleReset,
        isSubmitting,
        setFieldValue,
        values,
        resetForm,
      }) => (
        <>
          {isFeatureEnable('BULK_ENTITIES') && (
            <>
              <BulkTextModal
                open={bulkModalOpen}
                onClose={onBulkModalClose}
                onValidate={async (val) => {
                  await setFieldValue('name', val);
                  if (splitMultilines(val).length > 1) {
                    await setFieldValue('file', null);
                  }
                }}
                formValue={values.name}
              />
              <ProgressBar
                open={progressBarOpen}
                value={(bulkCurrentCount / bulkCount) * 100}
                label={`${bulkCurrentCount}/${bulkCount}`}
                title={t_i18n('Create multiple entities')}
                onClose={() => {
                  setProgressBarOpen(false);
                  resetForm();
                  resetBulk();
                  onCompleted?.();
                }}
              >
                <BulkResult variablesToString={(v) => v.input.name} />
              </ProgressBar>
            </>
          )}
          <Form style={{ margin: '20px 0 20px 0' }}>
            <Field
              component={isFeatureEnable('BULK_ENTITIES') ? BulkTextField : TextField}
              variant="standard"
              name="name"
              label={t_i18n('Name')}
              fullWidth={true}
              detectduplicate={['Vulnerability']}
              askAi={true}
            />
            <Field
              component={MarkdownField}
              name="description"
              label={t_i18n('Description')}
              fullWidth={true}
              multiline={true}
              rows="4"
              style={{ marginTop: 20 }}
              askAi={true}
            />
            <Field
              component={TextField}
              variant="standard"
              name="x_opencti_cvss_base_score"
              label={t_i18n('CVSS3 - Score')}
              type="number"
              step="0.1"
              fullWidth={true}
              style={{ marginTop: 20 }}
            />
            { /* TODO Dont touch until CVSS4 */}
            <Field
              component={SelectField}
              variant="standard"
              name="x_opencti_cvss_base_severity"
              label={t_i18n('CVSS3 - Severity')}
              fullWidth={true}
              containerstyle={{
                width: '100%',
                marginTop: 20,
              }}
            >
              <MenuItem value="CRITICAL">{t_i18n('CRITICAL')}</MenuItem>
              <MenuItem value="HIGH">{t_i18n('HIGH')}</MenuItem>
              <MenuItem value="MEDIUM">{t_i18n('MEDIUM')}</MenuItem>
              <MenuItem value="LOW">{t_i18n('LOW')}</MenuItem>
              <MenuItem value="Unknown">{t_i18n('Unknown')}</MenuItem>
            </Field>
            <Field
              component={TextField}
              variant="standard"
              name="x_opencti_cvss_attack_vector"
              label={t_i18n('CVSS3 - Attack vector')}
              fullWidth={true}
              style={{ marginTop: 20 }}
            />
            <ConfidenceField
              entityType="Vulnerability"
              containerStyle={fieldSpacingContainerStyle}
            />
            <Field
              component={SwitchField}
              type="checkbox"
              name="x_opencti_cisa_kev"
              label={t_i18n('CISA KEV')}
              fullWidth
              containerstyle={{ marginTop: 20 }}
            />
            <Field
              component={TextField}
              variant="standard"
              name="x_opencti_epss_score"
              label={t_i18n('EPSS Score')}
              type="number"
              fullWidth={true}
              style={{ marginTop: 20 }}
            />
            <Field
              component={TextField}
              variant="standard"
              name="x_opencti_epss_percentile"
              label={t_i18n('EPSS Percentile')}
              type="number"
              fullWidth={true}
              style={{ marginTop: 20 }}
            />
            <CreatedByField
              name="createdBy"
              style={{
                marginTop: 20,
                width: '100%',
              }}
              setFieldValue={setFieldValue}
            />
            <ObjectLabelField
              name="objectLabel"
              style={{
                marginTop: 20,
                width: '100%',
              }}
              setFieldValue={setFieldValue}
              values={values.objectLabel}
            />
            <ObjectMarkingField
              name="objectMarking"
              style={{
                marginTop: 20,
                width: '100%',
              }}
              setFieldValue={setFieldValue}
            />
            <ExternalReferencesField
              name="externalReferences"
              style={fieldSpacingContainerStyle}
              setFieldValue={setFieldValue}
              values={values.externalReferences}
            />
            <Field
              component={CustomFileUploader}
              name="file"
              setFieldValue={setFieldValue}
              disabled={splitMultilines(values.name).length > 1}
              noFileSelectedLabel={splitMultilines(values.name).length > 1
                ? t_i18n('File upload not allowed in bulk creation')
                : undefined
              }
            />
            <div style={{
              marginTop: '20px',
              textAlign: 'right',
            }}
            >
              <Button
                variant="contained"
                onClick={handleReset}
                disabled={isSubmitting}
                sx={{ marginLeft: 2 }}
              >
                {t_i18n('Cancel')}
              </Button>
              <Button
                variant="contained"
                color="secondary"
                onClick={submitForm}
                disabled={isSubmitting}
                sx={{ marginLeft: 2 }}
              >
                {t_i18n('Create')}
              </Button>
            </div>
          </Form>
        </>
      )}
    </Formik>
  );
};

const CreateVulnerabilityControlledDial = (props: DrawerControlledDialProps) => (
  <CreateEntityControlledDial entityType='Vulnerability' {...props} />
);

const VulnerabilityCreation: FunctionComponent<VulnerabilityCreationProps> = ({
  paginationOptions,
}) => {
  const { isFeatureEnable } = useHelper();
  const { t_i18n } = useFormatter();
  const [bulkOpen, setBulkOpen] = useState(false);
  const isFABReplaced = isFeatureEnable('FAB_REPLACEMENT');
  const updater = (store: RecordSourceSelectorProxy) => insertNode(
    store,
    'Pagination_vulnerabilities',
    paginationOptions,
    'vulnerabilityAdd',
  );

  return (
    <Drawer
      title={t_i18n('Create a vulnerability')}
      variant={isFABReplaced ? undefined : DrawerVariant.create}
      header={isFeatureEnable('BULK_ENTITIES')
        ? <BulkTextModalButton onClick={() => setBulkOpen(true)} />
        : <></>
      }
      controlledDial={isFABReplaced ? CreateVulnerabilityControlledDial : undefined}
    >
      {({ onClose }) => (
        <VulnerabilityCreationForm
          updater={updater}
          onCompleted={onClose}
          onReset={onClose}
          bulkModalOpen={bulkOpen}
          onBulkModalClose={() => setBulkOpen(false)}
        />
      )}
    </Drawer>
  );
};

export default VulnerabilityCreation;
